from typing import List, Dict, Optional
from .card import Card, Deck
from .player import Player
from .pot import PotManager
from .hand_evaluator import evaluate_hand, PokerHand

class TexasHoldem:
    """德州扑克游戏环境"""
    
    SMALL_BLIND = 10
    BIG_BLIND = 20
    MAX_ROUNDS = 1000  # 添加最大回合数限制
    BUYIN_AMOUNT = 1000  # 增加补码金额
    
    def __init__(self, players: List[Player]):
        if len(players) < 2:
            raise ValueError("至少需要2名玩家")
        self.players = players
        self.deck = Deck()
        self.community_cards: List[Card] = []
        self.total_pot = 0
        self.current_player_idx = 0
        self.dealer_idx = 0
        self.min_raise = self.BIG_BLIND
        self.round_counter = 0  # 添加回合计数器
        self.game_counter = 0  # 添加游戏局数计数器
        
        # 创建data目录
        import os
        os.makedirs('data', exist_ok=True)
        self.log_file = None
    
    def _log(self, message: str):
        """记录日志"""
        print(message)
        if self.log_file:
            self.log_file.write(message + '\n')
            self.log_file.flush()
    
    def reset(self):
        """重置游戏状态"""
        # 关闭上一局的日志文件
        if self.log_file:
            self.log_file.close()
        
        # 创建新的日志文件
        self.game_counter += 1
        self.log_file = open(f'data/game_{self.game_counter}.txt', 'w', encoding='utf-8')
        self._log(f"第 {self.game_counter} 局游戏开始")
        
        self.deck = Deck()
        self.community_cards = []
        self.total_pot = 0
        self.min_raise = self.BIG_BLIND
        self.round_counter = 0  # 重置回合计数器
        
        # 检查并处理破产玩家
        for player in self.players:
            if player.chips <= 0:
                print(f"\n{player.name} 筹码不足，自动补码 {self.BUYIN_AMOUNT}")
                player.chips = self.BUYIN_AMOUNT
        
        # 重置所有玩家
        for player in self.players:
            player.reset()
        
        # 移动庄家位置
        self.dealer_idx = (self.dealer_idx + 1) % len(self.players)
        self.current_player_idx = (self.dealer_idx + 3) % len(self.players)  # 大盲注后的玩家先行动
        
        # 发底牌
        for player in self.players:
            player.receive_cards(self.deck.deal(2))
        
        # 收取盲注
        sb_idx = (self.dealer_idx + 1) % len(self.players)
        bb_idx = (self.dealer_idx + 2) % len(self.players)
        
        self.total_pot += self.players[sb_idx].place_bet(min(self.SMALL_BLIND, self.players[sb_idx].chips))
        self.total_pot += self.players[bb_idx].place_bet(min(self.BIG_BLIND, self.players[bb_idx].chips))
        
        return self._get_state()
    
    def step(self, action: str, bet_amount: int = 0) -> tuple[dict, float, bool, dict]:
        """执行一步"""
        self.round_counter += 1
        if self.round_counter >= self.MAX_ROUNDS:
            return self._get_state(), 0, True, {"message": "达到最大回合数限制"}
            
        player = self.players[self.current_player_idx]
        
        # 如果玩家筹码为0且不是all in状态，自动弃牌
        if player.chips <= 0 and not player.is_all_in:
            action = 'fold'
            bet_amount = 0
        
        # 显示当前游戏状态
        self._display_game_state(player, action, bet_amount)
        
        reward = 0
        max_bet = max(p.current_bet for p in self.players)
        
        if action == 'fold':
            player.is_folded = True
        elif action == 'check':
            # 只有当前玩家下注等于最大下注时才能check
            if player.current_bet < max_bet:
                # 如果不能check,自动转为call
                action = 'call'
                call_amount = min(max_bet - player.current_bet, player.chips)  # 限制在玩家筹码范围内
                if call_amount > 0:
                    self.total_pot += player.place_bet(call_amount)
                    if player.chips == 0:
                        player.is_all_in = True
        elif action == 'call':
            call_amount = min(max_bet - player.current_bet, player.chips)  # 限制在玩家筹码范围内
            if call_amount > 0:
                self.total_pot += player.place_bet(call_amount)
                if player.chips == 0:
                    player.is_all_in = True
        elif action == 'raise':
            # 确保加注金额至少是最小加注
            min_raise_amount = max_bet - player.current_bet + self.min_raise
            if bet_amount < min_raise_amount:
                bet_amount = min_raise_amount
            bet_amount = min(bet_amount, player.chips)  # 限制在玩家筹码范围内
            self.total_pot += player.place_bet(bet_amount)
            if player.chips == 0:
                player.is_all_in = True
            self.min_raise = bet_amount - (max_bet - player.current_bet)  # 更新最小加注额
        
        # 移动到下一个未弃牌的玩家
        self._move_to_next_player()
        
        # 检查当前回合是否结束
        if self._is_round_complete():
            if len(self.community_cards) == 0:
                # 发翻牌
                self.community_cards.extend(self.deck.deal(3))
            elif len(self.community_cards) < 5:
                # 发转牌或河牌
                self.community_cards.extend(self.deck.deal(1))
            
            if len(self.community_cards) == 5:
                # 游戏结束，进行结算
                rewards = self._settle()
                reward = rewards[self.current_player_idx]
                done = True
            else:
                # 重置下注并继续
                for p in self.players:
                    p.current_bet = 0
                self.min_raise = self.BIG_BLIND
                done = False
        else:
            done = False
        
        return self._get_state(), reward, done, {}
    
    def _display_game_state(self, current_player: 'Player', action: str, bet_amount: int):
        """显示当前游戏状态"""
        # 显示阶段信息
        stage = "翻牌前" if len(self.community_cards) == 0 else \
               "翻牌圈" if len(self.community_cards) == 3 else \
               "转牌圈" if len(self.community_cards) == 4 else "河牌圈"
        
        self._log("\n" + "="*80)
        self._log(f"当前阶段: {stage}")
        
        # 显示公共牌
        if self.community_cards:
            self._log(f"公共牌: {' '.join(str(card) for card in self.community_cards)}")
        
        # 显示底池信息
        self._log(f"底池: {self.total_pot}")
        
        # 计算每个玩家的获胜概率
        active_players = [p for p in self.players if not p.is_folded]
        win_probs = {}
        for p in active_players:
            if hasattr(p, 'evaluator'):  # 对于AI玩家
                win_probs[p.name] = p.evaluator.calculate_win_probability(
                    p.hole_cards,
                    self.community_cards,
                    num_opponents=len(active_players)-1,
                    num_simulations=100
                )
        
        # 显示玩家信息
        roles = {
            (self.dealer_idx + 1) % len(self.players): "小盲注",
            (self.dealer_idx + 2) % len(self.players): "大盲注",
            self.dealer_idx: "庄家"
        }
        
        self._log("\n玩家状态:")
        for i, p in enumerate(self.players):
            role = roles.get(i, "")
            status = "已弃牌" if p.is_folded else \
                    "ALL IN" if p.is_all_in else "活跃"
            cards = f"[{' '.join(str(card) for card in p.hole_cards)}]" \
                   if p == current_player or p.is_folded else "[**]"
            
            # 添加获胜概率显示
            win_prob = f"胜率={win_probs.get(p.name, 0):.1%}" if p.name in win_probs else ""
            
            # 如果是当前玩家,显示手牌强度
            hand_strength = ""
            if p == current_player and hasattr(p, 'evaluator'):
                strength = p.evaluator.get_hand_strength(p.hole_cards, self.community_cards)
                hand_strength = f"手牌强度={strength:.2f}"
            
            # 添加统计信息
            stats_info = ""
            if hasattr(p, 'get_stats'):
                stats = p.get_stats()
                stats_info = (f"总盈利={stats['total_profit']} "
                            f"近期胜率={stats['recent_win_rate']:.1%} "
                            f"当前得分={stats['current_reward']:.1f}")
            
            self._log(f"{p.name} ({role}): 筹码={p.chips} 下注={p.current_bet} " \
                     f"状态={status} 手牌={cards} {win_prob} {hand_strength} | {stats_info}")
        
        # 显示当前行动
        self._log(f"\n当前行动: {current_player.name}")
        self._log(f"行动类型: {action}")
        if bet_amount > 0:
            self._log(f"下注金额: {bet_amount}")
        self._log("="*80)
    
    def _get_state(self) -> dict:
        """获取当前游戏状态"""
        return {
            'community_cards': self.community_cards,
            'total_pot': self.total_pot,
            'min_raise': self.min_raise,
            'current_player': self.current_player_idx,
            'players': [{
                'chips': p.chips,
                'current_bet': p.current_bet,
                'is_folded': p.is_folded,
                'is_all_in': p.is_all_in
            } for p in self.players]
        }
    
    def _move_to_next_player(self):
        """移动到下一个未弃牌的玩家"""
        active_players = [p for p in self.players if not p.is_folded]
        if len(active_players) <= 1:
            return
            
        while True:
            self.current_player_idx = (self.current_player_idx + 1) % len(self.players)
            if not self.players[self.current_player_idx].is_folded:
                break
    
    def _is_round_complete(self) -> bool:
        """检查当前回合是否结束"""
        # 获取未弃牌的玩家的最大下注额
        active_players = [p for p in self.players if not p.is_folded]
        if len(active_players) == 1:
            return True
            
        max_bet = max(p.current_bet for p in active_players)
        return all(p.current_bet == max_bet or p.is_all_in for p in active_players)
    
    def _settle(self) -> List[float]:
        """结算游戏，返回每个玩家的奖励"""
        active_players = [p for p in self.players if not p.is_folded]
        rewards = [0] * len(self.players)
        
        self._log("\n" + "="*40 + " 游戏结算 " + "="*40)
        
        if len(active_players) == 1:
            # 只有一个玩家未弃牌，直接获得所有底池
            winner = active_players[0]
            winner.win_pot(self.total_pot)
            rewards[self.players.index(winner)] = self.total_pot
            self._log(f"\n所有其他玩家都弃牌了")
            self._log(f"{winner.name} 获胜，赢得底池 {self.total_pot} 筹码")
        else:
            # 显示所有未弃牌玩家的手牌和公共牌
            self._log("\n底牌和公共牌:")
            self._log(f"公共牌: {' '.join(str(card) for card in self.community_cards)}")
            for player in active_players:
                self._log(f"{player.name} 的底牌: {' '.join(str(card) for card in player.hole_cards)}")
            
            # 创建边池管理器
            pot_manager = PotManager()
            
            # 收集所有玩家的下注
            total_bets = {}  # 记录每个玩家的总下注
            
            # 评估所有未弃牌玩家的手牌
            player_hands = {}
            self._log("\n各玩家牌型:")
            for player in active_players:
                hand = evaluate_hand(player.hole_cards, self.community_cards)
                player_hands[player] = hand
                self._log(f"{player.name}: {hand.hand_type} - 最佳组合: {' '.join(str(card) for card in hand.best_cards)}")
            
            # 分配奖池
            if self.total_pot > 0:  # 只要有底池就进行分配
                # 找出最大的手牌
                best_hand = max(player_hands.values())
                # 找出所有持有最大手牌的玩家
                winners = [p for p, h in player_hands.items() if h == best_hand]
                
                # 平分底池
                share = self.total_pot // len(winners)
                remainder = self.total_pot % len(winners)
                
                self._log("\n奖池分配:")
                for i, winner in enumerate(winners):
                    # 如果是最后一个赢家且有余数，加上余数
                    extra = remainder if i == len(winners) - 1 else 0
                    win_amount = share + extra
                    winner.win_pot(win_amount)
                    rewards[self.players.index(winner)] = win_amount
                    self._log(f"{winner.name} 赢得 {win_amount} 筹码")
            else:
                # 如果没有底池，显示平局信息
                self._log("本局无人下注，平局")
        
        # 显示每个玩家的最终筹码变化
        self._log("\n本局结果:")
        for player in self.players:
            profit = rewards[self.players.index(player)]
            if profit > 0:
                self._log(f"{player.name}: +{profit} 筹码 (现有筹码: {player.chips})")
            elif profit < 0:
                self._log(f"{player.name}: {profit} 筹码 (现有筹码: {player.chips})")
            else:
                self._log(f"{player.name}: 无变化 (现有筹码: {player.chips})")
        
        self._log("\n" + "="*90 + "\n")
        return rewards 